package com.fly.gateway.filter;

import com.fly.gateway.exception.SignatureException;
import com.fly.gateway.utils.SignUtil;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.StringUtils;

import javax.servlet.http.HttpServletRequest;
import java.util.*;

/**
 * @description 客户端签名过滤
 * @author: fly
 * @date: 2018/12/13 13:28
 */
public class CustomerSignatureFilter extends ZuulFilter {
    private final Logger log = LoggerFactory.getLogger(CustomerSignatureFilter.class);

    private static final String APP_KEY_NAME = "client_id";
    private static final String APP_SIGN_NAME = "sign";

    private static final HashMap<String, String> CLIENT_SECRET_MAP = new HashMap<String, String>() {{
        put("cloud_client", "secret_client");
        put("cloud_server", "secret_server");
        put("cloud_user", "secret_user");
    }};

    @Override
    public String filterType() {
        // 前置过滤器
        return "pre";
    }

    @Override
    public int filterOrder() {
        // 优先级为0，数字越大，优先级越低
        return 0;
    }

    @Override
    public boolean shouldFilter() {
        // 是否执行该过滤器，此处为true，说明需要过滤
        return true;
    }

    @Override
    public Object run() {
        RequestContext ctx = RequestContext.getCurrentContext();
        HttpServletRequest request = ctx.getRequest();

        boolean result = sign(request);
        // 暂时统一过滤成功
        if (true) {
            // 对该请求进行路由
            ctx.setSendZuulResponse(true);
            ctx.setResponseStatusCode(200);
            // 设值，让下一个Filter看到上一个Filter的状态
            ctx.set("isSuccess", true);
            return null;
        } else {
            // 过滤该请求，不对其进行路由
            ctx.setSendZuulResponse(false);
            // 返回错误码
            ctx.setResponseStatusCode(400);
            // 返回错误内容
            ctx.setResponseBody("{\"resultCode\": 400, \"resultMessages\": \"请求参数不合法\"}");
            ctx.set("isSuccess", false);
            return null;
        }
    }

    private boolean sign(HttpServletRequest request) {
        String clientId = request.getParameter(APP_KEY_NAME);
        if (StringUtils.isEmpty(clientId)) {
            // 没有clientId参数，不做过滤
            return true;
        }
        String url = request.getRequestURI();
        if (url.indexOf("/oauth/token") >= 0) {
            // 用户登陆，不做过滤
            return true;
        }
        String clientSecret = loadClientSecretById(clientId);
        if (StringUtils.isEmpty(clientSecret)) {
            // 没有找到对应clientSecret
            return false;
        }
        try {
            if (!signVerification(request, clientSecret)) {
                log.error("签名验证失败: client_id -> {}", clientId);
                return false;
            }
        } catch (Exception e) {
            log.error("签名验证出现异常: client_id -> {} \n message -> {}", clientId, e.getMessage());
            return false;
        }
        return true;
    }

    private String loadClientSecretById(String clientId) {
        return CLIENT_SECRET_MAP.get(clientId);
    }

    private boolean signVerification(HttpServletRequest request, String clientSecret) {
        String sign = request.getParameter(APP_SIGN_NAME);
        if (StringUtils.isEmpty(sign)) {
            throw new SignatureException(APP_SIGN_NAME + "参数不能为空");
        }

        Map<String, String[]> parameterMap = request.getParameterMap();

        Map<String, String> paramValues = getParamValues(parameterMap);

        List<String> ignoreParamNames = new ArrayList<>(16);
        ignoreParamNames.add(APP_SIGN_NAME);
        HashMap<String, String> signMap = SignUtil.sign(paramValues, ignoreParamNames,
                clientSecret);
        String appSign = signMap.get("appSign");
        if (!sign.equals(appSign)) {
            String appParam = signMap.get("appParam");
            throw new SignatureException("签名验证失败，sign: " + sign
                    + ", appSign:" + appSign + ", appParam:" + appParam);
        }

        return true;
    }

    private Map<String, String> getParamValues(Map<String, String[]> parameterMap) {
        Map<String, String> paramValues = new HashMap<>(16);
        Iterator iterator = parameterMap.entrySet().iterator();
        while (iterator.hasNext()) {
            Map.Entry entry = (Map.Entry) iterator.next();
            String key = (String) entry.getKey();
            String[] val = (String[]) entry.getValue();
            String value = "";
            if (val != null) {
                for (int i = 0; i < val.length; i++) {
                    value = val[i];
                    paramValues.put(key, value);
                }
            } else {
                paramValues.put(key, value);
            }
        }
        return paramValues;
    }
}
